package me.hekr.iotos.api.enums;

import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import me.hekr.iotos.api.dto.klink.AddTopo;
import me.hekr.iotos.api.dto.klink.BatchDevSend;
import me.hekr.iotos.api.dto.klink.CloudSend;
import me.hekr.iotos.api.dto.klink.DelTopo;
import me.hekr.iotos.api.dto.klink.DevLogin;
import me.hekr.iotos.api.dto.klink.DevLogout;
import me.hekr.iotos.api.dto.klink.DevSend;
import me.hekr.iotos.api.dto.klink.DevUpgrade;
import me.hekr.iotos.api.dto.klink.DevUpgradeProgress;
import me.hekr.iotos.api.dto.klink.GetConfig;
import me.hekr.iotos.api.dto.klink.GetConfigResp;
import me.hekr.iotos.api.dto.klink.GetTopo;
import me.hekr.iotos.api.dto.klink.Klink;
import me.hekr.iotos.api.dto.klink.KlinkResp;
import me.hekr.iotos.api.dto.klink.NotSupport;
import me.hekr.iotos.api.dto.klink.Register;
import me.hekr.iotos.api.dto.klink.RegisterResp;
import me.hekr.iotos.api.dto.klink.ReportFirmware;

public enum Action {
  NOT_SUPPORT("_notSupport_"),
  INNER_MQTT_SUB("innerMqttSub", FrameType.INNER),
  INNER_MQTT_PUB("innerMqttPub", FrameType.INNER),
  GATEWAY_LOGIN("gatewayLogin"),
  GATEWAY_LOGOUT("gatewayLogout"),
  DATA_CHANGED("dataChanged"),
  CLOUD_SEND("cloudSend", FrameType.DEV_DOWN, CloudSend.class),
  CLOUD_SEND_RESP("cloudSendResp", FrameType.DEV_UP, KlinkResp.class),
  DEV_SEND("devSend", FrameType.DEV_UP, DevSend.class),
  DEV_SEND_RESP("devSendResp", FrameType.DEV_DOWN),
  BATCH_DEV_SEND("batchDevSend", FrameType.DEV_UP, BatchDevSend.class),
  BATCH_DEV_SEND_RESP("batchDevSendResp", FrameType.DEV_DOWN),
  DEV_LOGIN("devLogin", FrameType.DEV_UP, DevLogin.class),
  DEV_LOGIN_RESP("devLoginResp", FrameType.DEV_DOWN),
  DEV_LOGOUT("devLogout", FrameType.DEV_UP, DevLogout.class),
  DEV_LOGOUT_RESP("devLogoutResp", FrameType.DEV_DOWN),
  ADD_TOPO("addTopo", FrameType.DEV_UP, AddTopo.class),
  ADD_TOPO_RESP("addTopoResp", FrameType.DEV_DOWN),
  GET_TOPO("getTopo", FrameType.DEV_UP, GetTopo.class),
  GET_TOPO_RESP("getTopoResp", FrameType.DEV_DOWN),
  DEL_TOPO("delTopo", FrameType.DEV_UP, DelTopo.class),
  DEL_TOPO_RESP("delTopoResp", FrameType.DEV_DOWN),
  REPORT_FIRMWARE("reportFirmware", FrameType.DEV_UP, ReportFirmware.class),
  REPORT_FIRMWARE_RESP("reportFirmwareResp", FrameType.DEV_DOWN),
  DEV_UPGRADE_PROGRESS("devUpgradeProgress", FrameType.DEV_UP, DevUpgradeProgress.class),
  DEV_UPGRADE_PROGRESS_RESP("devUpgradeProgressResp", FrameType.DEV_DOWN, KlinkResp.class),
  DEV_UPGRADE("devUpgrade", FrameType.DEV_DOWN, DevUpgrade.class),
  DEV_UPGRADE_RESP("devUpgradeResp", FrameType.DEV_UP, KlinkResp.class),
  REGISTER("register", FrameType.DEV_UP, Register.class),
  REGISTER_RESP("registerResp", FrameType.DEV_DOWN, RegisterResp.class),
  GET_CONFIG("getConfig", FrameType.DEV_UP, GetConfig.class),
  GET_CONFIG_RESP("getConfigResp", FrameType.DEV_DOWN, GetConfigResp.class);

  public static final String ACTION_NAME = "action";
  private static final Map<String, Action> ACTION_MAP =
      (Map)
          Arrays.stream(values()).collect(Collectors.toMap(Action::getAction, Function.identity()));
  private String action;
  private Class<? extends Klink> klinkClass;
  private FrameType frameType;

  private Action(String action, FrameType frameType, Class<? extends Klink> klinkClass) {
    this.action = action;
    this.frameType = frameType;
    this.klinkClass = klinkClass;
  }

  private Action(String action) {
    this(action, (FrameType) null, NotSupport.class);
  }

  private Action(String action, FrameType frameType) {
    this(action, frameType, NotSupport.class);
  }

  public static Action of(String action) {
    return (Action) ACTION_MAP.getOrDefault(action, NOT_SUPPORT);
  }

  public Action getPair() {
    if (this.isResp()) {
      return of(this.action.substring(0, this.getAction().indexOf("Resp")));
    } else {
      return this.isSend() ? of(this.action + "Resp") : this;
    }
  }

  public boolean isResp() {
    return this.action.endsWith("Resp");
  }

  public boolean isSend() {
    return this != NOT_SUPPORT && !this.isResp();
  }

  public boolean isAsyncResp() {
    return this == DEV_UPGRADE_RESP || this == CLOUD_SEND_RESP;
  }

  public String getAction() {
    return this.action;
  }

  public Class<? extends Klink> getKlinkClass() {
    return this.klinkClass;
  }

  public FrameType getFrameType() {
    return this.frameType;
  }
}
